package com.ruoyi.subject.controller;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.subject.constant.QbSubConstant;
import com.ruoyi.subject.domain.*;
import com.ruoyi.subject.service.IQbExamRecordNoService;
import com.ruoyi.subject.service.IQbExamRecordService;
import com.ruoyi.subject.service.IQbSubWrongService;
import com.ruoyi.subject.service.IQbSubjectService;
import com.ruoyi.subject.util.QbSubjectUtil;
import com.ruoyi.user.service.IQbUserWxService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.constraints.NotBlank;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 考试练习（真题，日常练习）Controller
 *
 * @author lzhj
 * @date 2021-01-27
 */
@Api(tags = "考试练习（真题，日常练习）")
@RestController
@RequestMapping("/qb/sub/practice")
@Validated
public class QbExamPracticeController extends BaseController {

    private static Logger logger = LoggerFactory.getLogger(QbExamPracticeController.class);

    @Autowired
    private IQbExamRecordService qbExamRecordService;


    @Autowired
    private IQbUserWxService qbUserWxService;

    @Autowired
    private IQbSubWrongService qbSubWrongService;


    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IQbSubjectService qbSubjectService;

    @Autowired
    private IQbExamRecordNoService qbExamRecordNoService;


    /**
     * 查询真题，无账号验证
     */
    @ApiOperation(value = "查询真题，无账号验证", notes = "返回数据：subjectRule：分数计算规则，subClass：题目类别，和返回的题目数据（subjects对象）的key匹配，weight：权重。subjects：题目数据,map对象，key为题目类别")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "zjh", value = "证件号", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "xm", value = "姓名", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "openId", value = "openid", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "subType", value = "题库类型", dataType = "string", paramType = "query"),
    })
    @PostMapping("realhListUnauth")
    public AjaxResult realhListUnauth(String zjh, String xm, String openId, @NotBlank String subType) {
        //验证用户信息
        if (StringUtils.isBlank(openId) || StringUtils.isBlank(xm)) {
            return AjaxResult.error("无权限");
        }
        JSONObject object = new JSONObject();
        //获取抽题规则,无规则，给个默认值和题目类别对应，前端代码逻辑使用了的
        List<SubjectRule> subjectRuleList = new ArrayList<>();
        SubjectRule  subjectRule = new SubjectRule();
        subjectRule.setSubClass("yk");
        subjectRuleList.add(subjectRule);
        //取题目

        Map<String, List<Map<String, Object>>> subjectMap = getSubjectByYk(subType);
        object.put("subjects", subjectMap);
        object.put("subjectRule", subjectRuleList);
        return AjaxResult.success("查询成功", object);
    }


    private Map<String, List<Map<String, Object>>> getSubjectByYk(String category) {
        //<题目类别，<题型，题目>>
        Map<String, List<Map<String, Object>>> subjectMap = new LinkedHashMap<>(10);
        List<Map<String, Object>> queTypeList = new ArrayList<>();
        subjectMap.put("yk", queTypeList);
        //
        List<QbSubject> subjectList = new ArrayList<>();
        //获得key
        Collection<String> keys = redisCache.keys(QbSubjectUtil.getSubjectCategoryCacheKey(category));
        for (String key : keys) {
            List<QbSubject> tempList = redisCache.getCacheObject(key);
            subjectList.addAll(tempList);
        }
        Map<String, List<QbSubject>> queTypeSubjects = subjectList.stream().collect(Collectors.groupingBy(QbSubject::getQueType));

        List<QbSubject> singleSubjects = getSubjectListByYk(queTypeSubjects,  QbSubConstant.QuestionType.single,30);
        Map<String, Object> queTypeMap = new LinkedHashMap<>(4);
        queTypeMap.put("code", QbSubConstant.QuestionType.single);
        queTypeMap.put("name", "单选");
        queTypeMap.put("subjects", singleSubjects);
        queTypeList.add(queTypeMap);

        List<QbSubject> multipleSubjects = getSubjectListByYk(queTypeSubjects,  QbSubConstant.QuestionType.multiple,30);
        Map<String, Object> queTypeMap2 = new LinkedHashMap<>(4);
        queTypeMap2.put("code", QbSubConstant.QuestionType.multiple);
        queTypeMap2.put("name", "多选");
        queTypeMap2.put("subjects", multipleSubjects);
        queTypeList.add(queTypeMap2);

        List<QbSubject> judgeSubjects = getSubjectListByYk(queTypeSubjects,  QbSubConstant.QuestionType.judge,30);
        Map<String, Object> queTypeMap3 = new LinkedHashMap<>(4);
        queTypeMap3.put("code", QbSubConstant.QuestionType.judge);
        queTypeMap3.put("name", "判断");
        queTypeMap3.put("subjects", judgeSubjects);
        queTypeList.add(queTypeMap3);
        return subjectMap;
    }

    /**
     * 游客练习抽题,取前n道题
     * @param queTypeSubjects
     * @param queType
     * @param num
     * @return
     */
    private List<QbSubject> getSubjectListByYk(Map<String, List<QbSubject>> queTypeSubjects, String queType, int num) {

        List<QbSubject> subjectList = queTypeSubjects.get(queType);
        List<QbSubject> retSubject = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(subjectList)) {
            subjectList.sort(Comparator.comparing(QbSubject::getId));
            //取前num个id
            if (subjectList.size() >= num) {
                retSubject = subjectList.subList(0, num);
            } else {
                retSubject = subjectList;
            }
        }
        return retSubject;
    }


    /**
     * 保存无账号练习的记录
     *
     * @param qbExamRecordNo
     * @return
     */
    @ApiOperation(value = "保存无账号练习的记录", notes = "")
    @Log(title = "考试记录-无账号", businessType = BusinessType.INSERT)
    @PostMapping("addRecordUnauth")
    public AjaxResult addRecordUnauth(@RequestBody QbExamRecordNo qbExamRecordNo) {
        if (StringUtils.isBlank(qbExamRecordNo.getOpenid()) || StringUtils.isBlank(qbExamRecordNo.getXm()) || StringUtils.isBlank(qbExamRecordNo.getSubType())) {
            return AjaxResult.error("无权限");
        }
        return toAjax(qbExamRecordNoService.insertQbExamRecordNo(qbExamRecordNo));
    }

    /**
     * 查询真题，日常练习
     */
    @ApiOperation(value = "查询真题", notes = "返回数据：subjectRule：分数计算规则，subClass：题目类别，和返回的题目数据（subjects对象）的key匹配，weight：权重。subjects：题目数据,map对象，key为题目类别")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "userId", value = "userId", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "openId", value = "openid", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "subType", value = "题库类型", dataType = "string", paramType = "query"),
    })
    @PostMapping("realList")
    public AjaxResult realList(long userId, @NotBlank String openId, @NotBlank String subType) {
        //验证用户信息
        if (!qbUserWxService.validataUserInfo(userId, openId)) {
            return QbSubjectUtil.getWxValidataFailMag();
        }
        JSONObject object = new JSONObject();
        //获取抽题规则
        List<SubjectRule> subjectRuleList = redisCache.getCacheObject(QbSubjectUtil.getSubjectRuleCacheKey(subType));
        if (CollectionUtils.isEmpty(subjectRuleList)) {
            logger.info("抽题规则获取失败");
            return AjaxResult.error("数据异常");
        }
        //取题目
        Map<String, List<Map<String, Object>>> subjectMap = getSubjectByrule(subjectRuleList);
        object.put("subjects", subjectMap);
        object.put("subjectRule", subjectRuleList);
        return AjaxResult.success("查询成功", object);
    }

    /**
     * 查询真题，日常练习
     */
    @ApiOperation(value = "查询日常练习")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "userId", value = "userId", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "openId", value = "openid", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "subType", value = "题库类型", dataType = "string", paramType = "query"),

    })
    @PostMapping("generalList")
    public AjaxResult generalList(long userId, @NotBlank String openId, @NotBlank String subType) {
        //验证用户信息
        if (!qbUserWxService.validataUserInfo(userId, openId)) {
            return QbSubjectUtil.getWxValidataFailMag();
        }
        JSONObject object = new JSONObject();
        //获取抽题规则
        List<SubjectRule> subjectRuleList = redisCache.getCacheObject(QbSubjectUtil.getSubjectRuleCacheKey(subType));
        if (CollectionUtils.isEmpty(subjectRuleList)) {
            logger.info("抽题规则获取失败");
            return AjaxResult.error("数据异常");
        }
        //取题目
        Map<String, List<Map<String, Object>>> subjectMap = getSubjectByrule(subjectRuleList);
        object.put("subjectMap", subjectMap);
        object.put("subjectRuleList", subjectRuleList);
        return AjaxResult.success("查询成功", object);
    }

    private Map<String, List<Map<String, Object>>> getSubjectByrule(List<SubjectRule> subjectRuleList) {
        //<题目类别，<题型，题目>>
        Map<String, List<Map<String, Object>>> subjectMap = new LinkedHashMap<>(10);
        if (CollectionUtils.isEmpty(subjectRuleList)) {
            return subjectMap;
        }
        for (SubjectRule subjectRule : subjectRuleList) {
            List<Map<String, Object>> queTypeList = new ArrayList<>();

            String subClass = subjectRule.getSubClass();
            subjectMap.put(subClass, queTypeList);

            Integer single = subjectRule.getSingle();
            List<QbSubject> singleSubjects = getSubjectListByrule(subjectRule.getCategory(), subClass, QbSubConstant.QuestionType.single, single);
            Map<String, Object> queTypeMap = new LinkedHashMap<>(4);
            queTypeMap.put("code", QbSubConstant.QuestionType.single);
            queTypeMap.put("name", "单选");
            queTypeMap.put("subjects", singleSubjects);
            queTypeList.add(queTypeMap);

            Integer multiple = subjectRule.getMultiple();
            List<QbSubject> multipleSubjects = getSubjectListByrule(subjectRule.getCategory(), subClass, QbSubConstant.QuestionType.multiple, multiple);
            Map<String, Object> queTypeMap2 = new LinkedHashMap<>(4);
            queTypeMap2.put("code", QbSubConstant.QuestionType.multiple);
            queTypeMap2.put("name", "多选");
            queTypeMap2.put("subjects", multipleSubjects);
            queTypeList.add(queTypeMap2);

            Integer judge = subjectRule.getJudge();
            List<QbSubject> judgeSubjects = getSubjectListByrule(subjectRule.getCategory(), subClass, QbSubConstant.QuestionType.judge, judge);
            Map<String, Object> queTypeMap3 = new LinkedHashMap<>(4);
            queTypeMap3.put("code", QbSubConstant.QuestionType.judge);
            queTypeMap3.put("name", "判断");
            queTypeMap3.put("subjects", judgeSubjects);
            queTypeList.add(queTypeMap3);
        }
        return subjectMap;
    }

    /**
     * 根据规则取题目
     *
     * @param category
     * @param subClass
     * @param queType
     * @param num
     * @return
     */
    private List<QbSubject> getSubjectListByrule(String category, String subClass, String queType, int num) {
        List<QbSubject> retSubject = new ArrayList<>();
        String singleKey = QbSubjectUtil.getSubjectTypeCacheKey(category, subClass, queType);
        List<QbSubject> subjectList = redisCache.getCacheObject(singleKey);
        if (CollectionUtils.isNotEmpty(subjectList)) {
            //随机取num个id
            Collections.shuffle(subjectList);
            if (subjectList.size() >= num) {
                retSubject = subjectList.subList(0, num);
            } else {
                retSubject = subjectList;
            }
        }
        return retSubject;
    }

    /**
     * 保存真题练习结果
     */
    @ApiOperation(value = "保存真题练习结果")
    @Log(title = "保存真题练习结果", businessType = BusinessType.INSERT)
    @PostMapping("saveRealPractice")
    public AjaxResult saveRealPractice(@RequestBody QbSubPraticeResult praticeResult) {
        if (!qbUserWxService.validataUserInfo(praticeResult.getUserId(), praticeResult.getOpenId())) {
            return QbSubjectUtil.getWxValidataFailMag();
        }
        Long userId = praticeResult.getUserId();
        String subType = praticeResult.getSubType();
        List<Long> wrongSubIdList = praticeResult.getWrongSubIdList();
        List<Long> correctSubIdList = praticeResult.getCorrectSubIdList();
        QbExamRecord qbExamRecor = praticeResult.getQbExamRecor();
        //查询错题
        QbSubWrong qbSubWrong = new QbSubWrong();
        qbSubWrong.setUserId(userId);
        List<QbSubWrong> qbSubWrongs = qbSubWrongService.selectQbSubWrongList(qbSubWrong);
        Map<Long, Long> subWrongMap = qbSubWrongs.stream().collect(Collectors.toMap(QbSubWrong::getSubjectId, QbSubWrong::getId));
        //移除正确的错题
        List<Long> removeWrongIdList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(correctSubIdList)) {
            for (Long subId : correctSubIdList) {
                if (subWrongMap.containsKey(subId)) {
                    removeWrongIdList.add(subWrongMap.get(subId));
                }
            }
        }
        //保存新增的错题
        List<QbSubWrong> addWrongList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(wrongSubIdList)) {
            for (Long subId : wrongSubIdList) {
                if (!subWrongMap.containsKey(subId)) {
                    QbSubWrong wrong = new QbSubWrong();
                    wrong.setUserId(userId);
                    wrong.setSubjectId(subId);
                    addWrongList.add(wrong);
                }
            }
        }

        if (CollectionUtils.isNotEmpty(removeWrongIdList)) {
            qbSubWrongService.deleteQbSubWrongByIds(removeWrongIdList.toArray(new Long[0]));
        }

        if (CollectionUtils.isNotEmpty(addWrongList)) {
            for (QbSubWrong wrong : addWrongList) {
                qbSubWrongService.insertQbSubWrong(wrong);
            }
        }

        //保存考试记录
        qbExamRecordService.insertQbExamRecord(qbExamRecor);
        return AjaxResult.success("成功");
    }

    /**
     * 保存日常练习结果
     */
    @ApiOperation(value = "保存日常练习结果")
    @Log(title = "保存日常练习结果", businessType = BusinessType.INSERT)
    @PostMapping("saveGeneralPractice")
    public AjaxResult savePractice(@RequestBody QbSubPraticeResult praticeResult) {
        if (!qbUserWxService.validataUserInfo(praticeResult.getUserId(), praticeResult.getOpenId())) {
            return QbSubjectUtil.getWxValidataFailMag();
        }
        Long userId = praticeResult.getUserId();
        String subType = praticeResult.getSubType();
        List<Long> wrongSubIdList = praticeResult.getWrongSubIdList();
        List<Long> correctSubIdList = praticeResult.getCorrectSubIdList();
        //查询错题
        QbSubWrong qbSubWrong = new QbSubWrong();
        qbSubWrong.setUserId(userId);
        List<QbSubWrong> qbSubWrongs = qbSubWrongService.selectQbSubWrongList(qbSubWrong);
        Map<Long, Long> subWrongMap = qbSubWrongs.stream().collect(Collectors.toMap(QbSubWrong::getSubjectId, QbSubWrong::getId));
        //移除正确的错题
        List<Long> removeWrongIdList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(correctSubIdList)) {
            for (Long subId : correctSubIdList) {
                if (subWrongMap.containsKey(subId)) {
                    removeWrongIdList.add(subWrongMap.get(subId));
                }
            }
        }
        //保存新增的错题
        List<QbSubWrong> addWrongList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(wrongSubIdList)) {
            for (Long subId : wrongSubIdList) {
                if (!subWrongMap.containsKey(subId)) {
                    QbSubWrong wrong = new QbSubWrong();
                    wrong.setUserId(userId);
                    wrong.setSubjectId(subId);
                    addWrongList.add(wrong);
                }
            }
        }

        if (CollectionUtils.isNotEmpty(removeWrongIdList)) {
            qbSubWrongService.deleteQbSubWrongByIds(removeWrongIdList.toArray(new Long[0]));
        }

        if (CollectionUtils.isNotEmpty(addWrongList)) {
            for (QbSubWrong wrong : addWrongList) {
                qbSubWrongService.insertQbSubWrong(wrong);
            }
        }
        return AjaxResult.success("成功");
    }


    @ApiOperation(value = "搜索题目")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "userId", value = "userId", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "openId", value = "openid", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "subType", value = "题库类型", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "pageNum", value = "当前记录起始索引", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "pageSize", value = "每页显示记录数", dataType = "int", paramType = "query"),
    })
    @PostMapping("searchSubject")
    public AjaxResult searchSubject(int pageNum, int pageSize, String search, long userId, @NotBlank String openId, @NotBlank String subType) {
        //验证用户信息
        if (!qbUserWxService.validataUserInfo(userId, openId)) {
            return QbSubjectUtil.getWxValidataFailMag();
        }
        List<QbSubject> subjectList = qbSubjectService.querySubjectByName(search, subType, pageNum, pageSize);
        int count = qbSubjectService.querySubjectCountByName(search, subType, pageNum, pageSize);
        TableDataInfo dataTable = new TableDataInfo();
        dataTable.setRows(subjectList);
        dataTable.setTotal(count);
        return AjaxResult.success("成功", dataTable);
    }


}
